package DyingBaby.backend.vm;

import DyingBaby.backend.tm.TransactionManager;

public class Visibility {
    
    /**
     * 判断当前记录版本是否应该被跳过（用于解决幻读问题）
     * @param tm 事务管理器，用于检查事务状态
     * @param t  当前事务对象
     * @param e  数据记录条目
     * @return true表示该版本应该被跳过，false表示不需要跳过
     */
    public static boolean isVersionSkip(TransactionManager tm, Transaction t, Entry e) {
        long xmax = e.getXmax(); // 获取删除/更新此记录的事务ID
        if(t.level == 0) {
            // 读未提交隔离级别不需要跳过任何版本
            return false;
        } else {
            // 可重复读隔离级别下：
            // 1. 如果xmax事务已提交
            // 2. 且xmax大于当前事务ID 或 xmax在当前事务的快照中
            // 则需要跳过该版本（解决幻读）
            return tm.isCommitted(xmax) && (xmax > t.xid || t.isInSnapshot(xmax));
        }
    }

    /**
     * 根据事务隔离级别判断记录可见性
     * @param tm 事务管理器
     * @param t  当前事务
     * @param e  数据记录
     * @return true表示对当前事务可见，false表示不可见
     */
    public static boolean isVisible(TransactionManager tm, Transaction t, Entry e) {
        if(t.level == 0) {
            // 读未提交隔离级别使用读已提交的可见性判断（但实际会读到未提交数据）
            return readCommitted(tm, t, e);
        } else {
            // 可重复读隔离级别使用更强的可见性判断
            return repeatableRead(tm, t, e);
        }
    }

    /**
     * 读已提交隔离级别的可见性判断规则
     */
    private static boolean readCommitted(TransactionManager tm, Transaction t, Entry e) {
        long xid = t.xid;         // 当前事务ID
        long xmin = e.getXmin();  // 创建此记录版本的事务ID
        long xmax = e.getXmax();  // 删除/更新此记录的事务ID

        // 情况1：当前事务创建了该版本且尚未被删除
        if(xmin == xid && xmax == 0) return true;

        // 情况2：创建该版本的事务已提交
        if(tm.isCommitted(xmin)) {
            if(xmax == 0) {
                // 2.1 该版本未被删除 -> 可见
                return true;
            }
            if(xmax != xid) {
                // 2.2 该版本被其他事务删除：
                //     如果删除事务未提交 -> 可见（因为删除还未生效）
                if(!tm.isCommitted(xmax)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 可重复读隔离级别的可见性判断规则
     */
    private static boolean repeatableRead(TransactionManager tm, Transaction t, Entry e) {
        long xid = t.xid;
        long xmin = e.getXmin();
        long xmax = e.getXmax();

        // 情况1：当前事务创建了该版本且尚未被删除
        if(xmin == xid && xmax == 0) return true;

        // 情况2：满足以下所有条件时可见：
        //   a) 创建该版本的事务已提交
        //   b) 创建事务ID小于当前事务ID（避免读到未来事务的数据）
        //   c) 创建事务不在当前事务的快照中（避免读到本事务开始后提交的数据）
        if(tm.isCommitted(xmin) && xmin < xid && !t.isInSnapshot(xmin)) {
            if(xmax == 0) {
                // 2.1 该版本未被删除 -> 可见
                return true;
            }
            if(xmax != xid) {
                // 2.2 满足以下任一条件即可见：
                //   a) 删除事务未提交
                //   b) 删除事务ID大于当前事务ID（属于未来事务）
                //   c) 删除事务在当前事务的快照中
                if(!tm.isCommitted(xmax) || xmax > xid || t.isInSnapshot(xmax)) {
                    return true;
                }
            }
        }
        return false;
    }
}